package lexis

import scala.collection.mutable.ArrayBuffer
import java.lang.StringBuilder
import CLexicalException._
import const.CConstCommons._

class CLexicalAnalyzer(private var text:String)
{
    private var _text = new java.lang.StringBuilder(text)
    private val lexemes = ArrayBuffer[CLexeme]()

    def apply():Seq[CLexeme] =
    {
        checkForTerminator()
        addTerminator()
        replaceTabs()
        while(_text.length() != 0)
            if (CLexemeType find checkAndMove isEmpty) CLexicalException(WRONG_TOKEN(_text.toString))
        filter()
    }

    private def checkAndMove(tokenType:CLexemeType):Boolean =
    {
        (tokenType.pattern findFirstIn _text) foreach (t =>
        {
            if (tokenType.value exists(_ != t) unary_!)
            {
                lexemes += CLexeme(tokenType, t)
                _text.replace(0, t.size, "")
                return true
            }
        })
        false
    }

    private def checkForTerminator() = if (text contains TERMINATOR) CLexicalException(WRONG_TOKEN(text))

    private def addTerminator() = _text.append(TERMINATOR)

    private def replaceTabs()
    {
        val lns = _text.toString split NEW_LINE
        if (lns isEmpty) return
        val txt = new StringBuilder
        val cnt = lns.foldLeft(0)((c, l) =>
        {
            val crntCnt = tabCount(l)
            val ln = l filter (c => c != TAB)
            if (crntCnt > c)
            {
                for (_ <- 0 until crntCnt - c) txt.append(LEFT_BRACKET)
                txt.append(s"$NEW_LINE$ln")
            }
            else if (crntCnt < c)
            {
                for (_ <- 0 until c - crntCnt) txt.append(RIGHT_BRACKET)
                txt.append(s"$NEW_LINE$ln")
            }
            else txt.append(s"$NEW_LINE$ln")
            crntCnt
        })
        for (_ <- 0 until cnt) txt.append(RIGHT_BRACKET)
        _text = txt
    }

    private def tabCount(string:String):Int = string.foldLeft(0)((c, s) => if (s == TAB) c + 1 else return c)

    private def filter() = lexemes filter (l => l.lexemeType != CLexemeType.SPACE && l.lexemeType != CLexemeType.NEW_LINE)
}

object CLexicalAnalyzer
{
    def apply(text:String) = (new CLexicalAnalyzer(text))()
}
